πŸ•ΈοΈ Ada Research Browser

helm-conventions.md
← Back

Helm Chart Conventions for SRE

Read this before creating or modifying Helm charts in apps/templates/.

Chart Structure

Every app template chart follows this layout:

apps/templates/<chart-name>/
β”œβ”€β”€ Chart.yaml
β”œβ”€β”€ values.yaml
β”œβ”€β”€ values.schema.json          # REQUIRED β€” JSON Schema for values validation
β”œβ”€β”€ README.md
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ _helpers.tpl
β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”œβ”€β”€ service.yaml
β”‚   β”œβ”€β”€ serviceaccount.yaml
β”‚   β”œβ”€β”€ hpa.yaml
β”‚   β”œβ”€β”€ pdb.yaml
β”‚   β”œβ”€β”€ networkpolicy.yaml
β”‚   β”œβ”€β”€ servicemonitor.yaml
β”‚   β”œβ”€β”€ virtualservice.yaml     # Istio ingress (if app has external traffic)
β”‚   β”œβ”€β”€ authorizationpolicy.yaml # Istio RBAC (for internal APIs)
β”‚   β”œβ”€β”€ externalsecret.yaml     # ESO secret sync from OpenBao
β”‚   └── NOTES.txt
└── tests/
    └── test-connection.yaml

Chart.yaml

apiVersion: v2
name: sre-web-app
description: SRE standard Helm chart for web applications
type: application
version: 0.1.0        # Chart version β€” bump on chart changes
appVersion: "1.0.0"   # Default app version β€” overridden per deployment

values.yaml Conventions

Structure values in logical groups. Every value that a developer needs to set should be at the top level under app:.

# --- Developer-facing values (what app teams configure) ---
app:
  name: ""                    # REQUIRED
  team: ""                    # REQUIRED
  image:
    repository: ""            # REQUIRED β€” e.g., harbor.sre.internal/teamname/appname
    tag: ""                   # REQUIRED β€” e.g., v1.2.3
    pullPolicy: IfNotPresent
  port: 8080
  replicas: 2
  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 500m
      memory: 512Mi
  env: []
  #  - name: DATABASE_URL
  #    secretRef: my-db-secret   # Synced from OpenBao via ESO
  probes:
    liveness:
      path: /healthz
      initialDelaySeconds: 10
    readiness:
      path: /readyz
      initialDelaySeconds: 5

# --- Ingress ---
ingress:
  enabled: false
  host: ""                     # e.g., my-app.apps.sre.example.com
  gateway: istio-system/main   # Istio gateway reference

# --- Autoscaling ---
autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilization: 80

# --- Platform integration (usually not changed by app teams) ---
serviceMonitor:
  enabled: true
  interval: 30s
  path: /metrics

networkPolicy:
  enabled: true
  additionalIngress: []
  additionalEgress: []

podDisruptionBudget:
  enabled: true
  minAvailable: 1

values.schema.json

REQUIRED for every chart. This validates values at install time and provides documentation.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["app"],
  "properties": {
    "app": {
      "type": "object",
      "required": ["name", "team", "image"],
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Application name, used for resource naming"
        },
        "team": {
          "type": "string",
          "minLength": 1,
          "description": "Owning team name, used for labels and RBAC"
        },
        "image": {
          "type": "object",
          "required": ["repository", "tag"],
          "properties": {
            "repository": {
              "type": "string",
              "pattern": "^harbor\\.sre\\.internal/",
              "description": "Must be from the internal Harbor registry"
            },
            "tag": {
              "type": "string",
              "minLength": 1,
              "not": { "const": "latest" },
              "description": "Image tag β€” must be pinned, never latest"
            }
          }
        }
      }
    }
  }
}

Security Context β€” MANDATORY

Every Deployment template MUST include this security context. See @docs/agent-docs/security-contexts.md for the full reference.

spec:
  template:
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: {{ .Values.app.name }}
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsNonRoot: true
            capabilities:
              drop:
                - ALL

Labels β€” MANDATORY

Every resource MUST have these labels:

metadata:
  labels:
    app.kubernetes.io/name: {{ .Values.app.name }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/version: {{ .Values.app.image.tag }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
    app.kubernetes.io/part-of: sre-platform
    sre.io/team: {{ .Values.app.team }}

NetworkPolicy β€” MANDATORY

Every chart includes a NetworkPolicy. Default: deny all, allow from Istio gateway (if ingress enabled) and from monitoring namespace.

ExternalSecret Template

For apps that need secrets from OpenBao:

{{- range .Values.app.env }}
{{- if .secretRef }}
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: {{ .secretRef }}
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: openbao-backend
    kind: ClusterSecretStore
  target:
    name: {{ .secretRef }}
  data:
    - secretKey: value
      remoteRef:
        key: sre/{{ $.Values.app.team }}/{{ .secretRef }}
{{- end }}
{{- end }}

Testing Charts

# Lint
helm lint apps/templates/<chart>/

# Template render (dry-run)
helm template test apps/templates/<chart>/ -f test-values.yaml

# Unit tests (requires helm-unittest plugin)
helm unittest apps/templates/<chart>/

# Schema validation
helm install --dry-run --debug test apps/templates/<chart>/ -f test-values.yaml

Common Mistakes